package org.syntaxlisp.core.match;

import org.syntaxlisp.core.element.DefID;
import org.syntaxlisp.core.utils.CommonUtils;
import org.syntaxlisp.core.utils.EqualsTo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MatchUnit implements EqualsTo {

    private MatchUnitType type;

    /**
     * 在 type 为 BASE 的情况下不为空，该值为 BASE NAME
     */
    private String name;

    /**
     * 在 type 为 BASE 的情况下不为空
     */
    private List<MatchUnit> subs;

    private PositionScope srcPositionScope;

    /**
     * 在 type 为 STRING 的情况下不为空，该值为匹配的 string value
     */
    private Object val;

    private DefID baseId;

    public void determineDefId (Map<String, DefID> defIDMap) {
        if (type == MatchUnitType.BASE) {
            baseId = defIDMap.get(name);
        }
    }

    public MatchUnit getSub(int index) {
        return subs.get(index);
    }

    public void genSourcePositionScope () {
        if (!CollectionUtils.isEmpty(subs)) {
            srcPositionScope = PositionScope.newInstance();
            for (MatchUnit sub : subs) {
                srcPositionScope.addScope(sub.getSrcPositionScope());
            }
        }
    }

    public void genSourcePositionScope (List<PositionScope> subPositions) {
        if (!CollectionUtils.isEmpty(subPositions)) {
            srcPositionScope = PositionScope.newInstance();
            for (PositionScope subPosition : subPositions) {
                srcPositionScope.addScope(subPosition);
            }
        }
    }

    public String mergeValue() {
        if (MatchUnitType.BASE == type) {
            return subs.stream().map(MatchUnit::mergeValue).collect(Collectors.joining());
        } else {
            return (String) val;
        }
    }

    public boolean equalsTo (MatchUnit matchUnit) {
        return type == matchUnit.getType()
               && CommonUtils.equals(name, matchUnit.name)
               && CommonUtils.equalsTo(subs, matchUnit.subs)
               && CommonUtils.equalsTo(srcPositionScope, matchUnit.srcPositionScope)
               && CommonUtils.equals(val, matchUnit.val);
    }

    @Override
    public boolean equalsTo(EqualsTo o) {
        if (!(o instanceof MatchUnit)) {
            return false;
        }
        return equalsTo(o);
    }
}
